package com.sia.springcloud.protocol;

import com.sia.springcloud.model.Device;
import com.sia.springcloud.model.Point;
import com.sia.springcloud.protocol.model.OpcuaDevice;
import com.sia.springcloud.protocol.model.OpcuaPoint;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

/**
 * @Author: Sirius
 * @Date: 2022/1/2 15:20
 * @Description:
 **/
@Slf4j
@Data
@Component
public class Opcua implements IProtocol {

    private Long id = 18813435L;

    private static volatile Map<Integer, OpcUaClient> clientMap = new HashMap<>(64);
    private static volatile int collectSum = 0;


    @Override
    public String read(Device device, Point point) throws Exception {
        OpcuaDevice opcuaDevice = OpcuaDevice.reconvert(device);
        OpcuaPoint opcuaPoint = OpcuaPoint.reconvert(point);


        Integer namespace = opcuaPoint.getNamespace();
        String tag = opcuaPoint.getTagName();


        NodeId nodeId = new NodeId(namespace, tag);

        CompletableFuture<String> value = new CompletableFuture<>();

        //获取opcuaClient并连接
        OpcUaClient client = getOpcUaClient(opcuaDevice);
        client.connect().get();


        client.readValue(0.0, TimestampsToReturn.Both, nodeId).thenAccept(dataValue -> {
            try {
                value.complete(dataValue.getValue().getValue().toString());
            } catch (Exception e) {
                log.error("accept point(ns={};s={}) value error: {}", namespace, tag, e.getMessage());
            }
        });
        String rawValue = value.get(1, TimeUnit.SECONDS);
        log.debug("read point(ns={};s={}) value: {}", namespace, tag, rawValue);
        return rawValue;
    }


    @Override
    public Boolean write(Device device, Point point) throws Exception {

        //writeItem(device.getId(), driverInfo, pointInfo, value);
        return true;
    }


    /**
     * Get Opc Ua Client
     *
     * @return OpcUaClient
     * @throws UaException UaException
     */
    private OpcUaClient getOpcUaClient(OpcuaDevice opcuaDevice) {
        Integer deviceId = opcuaDevice.getId();

        OpcUaClient opcUaClient = clientMap.get(deviceId);
        if (null == opcUaClient) {
            String host = opcuaDevice.getHost();
            Integer port = opcuaDevice.getPort();

            log.info("这里的path还没有确定是哪个字段");

            String path = "";

            try {
                //如果没有就新建一个opcuaClient
                //OpcUaClient.create()
                opcUaClient = OpcUaClient.create(
                        String.format("opc.tcp://%s:%s%s", host, port, path),
                        endpoints -> endpoints.stream().findFirst(),
                        configBuilder -> configBuilder
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(uint(5000))
                                .build()
                );
                clientMap.put(deviceId, opcUaClient);
            } catch (Exception e) {
                log.error("get opc ua client error: {}", e.getMessage());
                clientMap.entrySet().removeIf(next -> next.getKey().equals(deviceId));
            }
        }
        return clientMap.get(deviceId);
    }


}
